package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.common.pojo.Result;
import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.pojo.Sku;
import com.changgou.search.dao.SkuEsMapper;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class SkuServiceImpl implements SkuService {
  // 注入feign
  @Autowired private SkuFeign skuFeign;
  // 注入mapper
  @Autowired private SkuEsMapper skuEsMapper;
  @Autowired private ElasticsearchTemplate elasticsearchTemplate;

  /** 导入到索引库 */
  @Override
  public void importData() {
    // 调用feign，查询list<sku>
    Result<List<Sku>> skuList = skuFeign.findAll();
    // 将list<sku>转换成list<skuinfo>
    List<SkuInfo> skuInfos = JSON.parseArray(JSON.toJSONString(skuList.getData()), SkuInfo.class);
    // 循环当前的sku
    for (SkuInfo skuInfo : skuInfos) {
      // 转换成map
      Map<String, Object> specMap = JSON.parseObject(skuInfo.getSpec(), Map.class);
      skuInfo.setSpecMap(specMap);
    }
    // 调用到实现数据导入
    skuEsMapper.saveAll(skuInfos);
  /*  for (SkuInfo skuInfo : skuInfos) {

      BeanUtils.copyProperties(lemBook, esLemBook);
      IndexQuery indexQuery = new IndexQuery();
      indexQuery.setId(esLemBook.getId().toString());
      indexQuery.setObject(esLemBook);
      indexQuery.setIndexName("book");
      indexQuery.setType("book");
      queryList.add(indexQuery);
      if(count%1000==0){
        esTemplate.bulkIndex(queryList);
        queryList.clear();
      }
      count++;
    }
    if(queryList.size()>0){
      esTemplate.bulkIndex(queryList);
    }
      //
    }*/
  }

  /**
   * 搜索
   *
   * @param searchMap
   * @retur
   *
   */
  @Override
  public Map<String, Object> search(Map<String, String> searchMap) {
    // 条件查询
    NativeSearchQueryBuilder builder = buildBasicQuery(searchMap);
    // 集合搜索
    Map<String, Object> resultMap = searchList(builder);
    /**
     * 获取广告品牌规格数据的优化
     */
    Map<String, Object> gropMap = searchGroudList(builder, searchMap);
    resultMap.putAll(gropMap);
    return resultMap;
  }
  private Map<String,Object> searchGroudList(NativeSearchQueryBuilder builder,Map<String,String> searchMap) {
    /** 分组查询分类数据 addAggregation()添加一个聚合函数 terms()取别名 */
    if (searchMap == null || StringUtils.isEmpty(searchMap.get("category"))){
      builder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
    }
    if (searchMap == null || StringUtils.isEmpty(searchMap.get("brand"))){
      builder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
    }
    builder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword"));
    // 执行搜索
    AggregatedPage<SkuInfo> aggregatedPage =
            elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
    Map<String,Object> groupSearchMap= new HashMap<>();
    /** 获得分组的数据 */
    if (searchMap == null || StringUtils.isEmpty(searchMap.get("category"))){
      StringTerms categoryTerms = aggregatedPage.getAggregations().get("skuCategory");
      List<String> categoryList = groupList(categoryTerms);
      groupSearchMap.put("categoryList",categoryList);

    }
    /**
     * 获取品牌数据
     */
    if (searchMap == null || StringUtils.isEmpty(searchMap.get("brand"))){
      StringTerms brandTerms = aggregatedPage.getAggregations().get("skuBrand");
      List<String> brandList = groupList(brandTerms);
      groupSearchMap.put("brandList",brandList);
    }
    /** 获取规格的数据**/
    StringTerms specTerms = aggregatedPage.getAggregations().get("skuSpec");
    List<String> specList = groupList(specTerms);
    Map<String, Set<String>> specMap = putAllSpec(specList);
    groupSearchMap.put("specList",specMap);
    return groupSearchMap;
  }
  /**
   * 获取分组的集合的数据
   */
  public  static List<String> groupList(StringTerms  stringTerms){
    List<String> groupList = new ArrayList<>();
    // 遍历分组的集合
    for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
      // 其中的一个分类的名称
      String bgroupName = bucket.getKeyAsString();
      // 把分类的名称添加到集合中
      groupList.add(bgroupName);
    }
    return  groupList;
  }
  /**
   * 条件查询
   *
   * @param searchMap
   * @return
   */
  private NativeSearchQueryBuilder buildBasicQuery(Map<String, String> searchMap) {
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    // 构建组合查询
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    // map不为空
    if (searchMap.size() > 0 && searchMap != null) {
      // 根据关键字搜索
      String keywords = searchMap.get("keywords");
      // 如果关键字不为空
      if (!StringUtils.isEmpty(keywords)) {
        // 构建查询条件
        boolQueryBuilder.must(QueryBuilders.queryStringQuery(keywords).field("name"));
      }
      // 分类搜索
      if (!StringUtils.isEmpty(searchMap.get("category"))) {
        boolQueryBuilder.must(QueryBuilders.termQuery("categoryName", searchMap.get("category")));
      }
      // 输入了品牌
      if (!StringUtils.isEmpty(searchMap.get("brand"))) {
        boolQueryBuilder.must(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
      }
      // 按照规格进行查询
      for (Map.Entry<String, String> entry : searchMap.entrySet()) {
        // 获得key
        String key = entry.getKey();
        // 如果这个key 是以spec_开头
        if (key.startsWith("spec_")) {
          // 进行组合查询 key ：specMap.产品型号.keyword
          boolQueryBuilder.must(
              QueryBuilders.termQuery(
                  "specMap." + key.substring(5) + ".keyword", entry.getValue()));
        }
      }
      // 按照价格进行搜索
      String price = searchMap.get("price");
      if (StringUtils.isNotEmpty(price)) {
        // 去掉以上跟元
        price = price.replace("元", "").replace("以上", "");
        // 进行字符串切割
        String[] prices = price.split("-");
        if (prices != null && prices.length > 0) {
          // 构建查询条件 查询的数据是3000
          // 查询出来的数据是大于prices[0]
          boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(prices[0])));
          int pricesLength = 2;
          if (prices.length == pricesLength) {
            // 小于等于prices[1]的数据
            boolQueryBuilder.must(
                QueryBuilders.rangeQuery("price").lte(Integer.parseInt(prices[1])));
          }
        }
      }
      // 排序的实现
      // 指定排序的域
      String sortFiled = searchMap.get("sortFiled");
      // 排序的规则
      String sortRule = searchMap.get("sortRule");
      if (!StringUtils.isEmpty(sortFiled) && !StringUtils.isEmpty(sortRule) ){
          builder.withSort(new FieldSortBuilder(sortFiled).order(SortOrder.valueOf(sortRule)));
      }

    }
      // 分页查询
      Integer pageNum = coverterPage(searchMap);
      Integer pageSize= 15;
     builder.withPageable(PageRequest.of(pageNum-1,pageSize));
    // 将boolQueryBuilder填充给NativeSearchQueryBuilder
      builder.withQuery(boolQueryBuilder);

    return builder;
  }

  /**
   * 分页查询
   * @param searchMap
   * @return
   */
  public  Integer coverterPage(Map<String,String> searchMap){
      if (searchMap!=null){
        String pageNum = searchMap.get("pageNum");
        try{
          if (!StringUtils.isEmpty(pageNum)){
            return Integer.parseInt(pageNum);
          }
        }
        catch (Exception e)
        {
          e.printStackTrace();
        }

      }
      return 1;
  }

  /**
   * 集合搜索
   *
   * @param builder
   * @return
   */
  private Map<String, Object> searchList(NativeSearchQueryBuilder builder) {
     // 高亮搜索
    HighlightBuilder.Field field = new HighlightBuilder.Field("name");
    // 前缀
    field.preTags("<em style=\"color:red;\">");
    //后缀
    field.postTags("</em>");
    //碎片长度，就是关键字的长度
    field.fragmentSize(100);
    //添加高亮
    builder.withHighlightFields(field);

    // 执行搜索
    AggregatedPage<SkuInfo> page =
        elasticsearchTemplate.queryForPage(
            builder.build(), //
            SkuInfo.class, // 数据集合要转换的字节码
            new SearchResultMapper() { // 执行搜索后将数据封装到这个对象中
              @Override
              public <T> AggregatedPage<T> mapResults(
                  SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                // 存储转后的所有高亮数据
                List<T> list = new ArrayList<>();
                // 执行查询，获取所有的数据-》结果集高亮非高
                SearchHits hits = searchResponse.getHits();
                System.out.println("数据是"+hits.getTotalHits());
                System.out.println("数据是"+hits.getHits().length);
                for (SearchHit hit : hits) {
                  // 分析结果集，获取非高亮的数据
                  SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
                  // 分析结果集数据获取高亮数据
                  HighlightField highlightField = hit.getHighlightFields().get("name");

                  if (highlightField != null && highlightField.getFragments() != null) {
                    // 高亮数据读出来
                    Text[] fragments = highlightField.getFragments();
                    StringBuffer stringBuffer = new StringBuffer();
                    for (Text fragment : fragments) {
                      stringBuffer.append(fragment.toString());
                    }
                    // 替换非高亮数据中指定的域
                    skuInfo.setName(stringBuffer.toString());
                  }
                  // 添加高亮的数据到集合中
                  list.add((T) skuInfo);
                }
                /*高亮
                 * list 这个是高亮的的数据
                 * pageable 当前的页码
                 * searchResponse.getHits().getTotalHits() 总页数
                 * */
                return new AggregatedPageImpl<>(
                    list, pageable, hits.getTotalHits());
              }
            });
        
    // 分页参数 - 总页数
    long totalElements = page.getTotalElements();
    // 总页数
    int totalPages = page.getTotalPages();
    // 获取数据的结果集
    List<SkuInfo> skuinfoList = page.getContent();
    // 封装一个map获得所有数据并且返回去
    Map<String, Object> resultMap = new HashMap<>();
    resultMap.put("rows", skuinfoList);
    resultMap.put("total", totalElements);
    resultMap.put("totalPages", totalPages);
    /* 获取分页的数据*/
    NativeSearchQuery build = builder.build();
    Pageable pageable = build.getPageable();
    int pageNumber = pageable.getPageNumber();
    int pageSize = pageable.getPageSize();
    resultMap.put("pageNumber", pageNumber);
    resultMap.put("pageSize", pageSize);
    return resultMap;
  }
  /**
   * 规格汇总
   *
   * @param specList
   * @return
   */
  private Map<String, Set<String>> putAllSpec(List<String> specList) {
    // 3.将每个map对象合成一个新的Map<String,set<String>>
    Map<String, Set<String>> allSpec = new HashMap<>();
    // 1. 循环specList
    for (String spec : specList) {
      // 2. 将每个json字符串抓换成map
      Map<String, String> specMap = JSON.parseObject(spec, Map.class);
      // 4. 合并流程
      // 4.1循环所有的map，
      for (Map.Entry<String, String> entry : specMap.entrySet()) {
        // 4.2 取出当前的Map,并且获得对应的key
        // 规格的名称
        String key = entry.getKey();
        // 规格的值
        String value = entry.getValue();
        Set<String> specSet = allSpec.get(key);
        if (specSet == null) {
          specSet = new HashSet<String>();
        }
        // 4.3将当前的循环的数合并到一个新的Map<String,set<String>>
        specSet.add(value);
        allSpec.put(key, specSet);
      }
    }
    return allSpec;
  }
}
